昨天我們撰寫了 interface
方法後,今天要來實作他們,就讓我們開始吧!
在 /module/user
底下建立一個名為 repository
的資料夾,並在裡面建立一個名為 repository.go
的檔案,在此處我們會 implement 前面步驟宣告的 interface
,內容如下
package repository
import (
"github.com/jinzhu/gorm"
"sample_api/model"
"sample_api/module/user"
)
type UserRepository struct {
orm *gorm.DB
}
func NewUserRepository(orm *gorm.DB) user.Repository {
return &UserRepository{
orm: orm,
}
}
func (u *UserRepository) GetUserList(data map[string]interface{}) ([]*model.User, error) {
var (
err error
in = make([]*model.User, 0)
)
err = u.orm.Find(&in, data).Error
return in, err
}
func (u *UserRepository) GetUser(in *model.User) (*model.User, error) {
var err error
err = u.orm.First(&in).Error
return in, err
}
func (u *UserRepository) CreateUser(in *model.User) (*model.User, error) {
var err error
err = u.orm.Create(&in).Error
return in, err
}
func (u *UserRepository) UpdateUser(in *model.User) (*model.User, error) {
var err error
err = u.orm.Save(&in).Error
return in, err
}
func (u *UserRepository) ModifyUser(in *model.User, data map[string]interface{}) (*model.User, error) {
var err error
err = u.orm.Model(&in).Updates(data).Error
return in, err
}
func (u *UserRepository) DeleteUser(in *model.User) error {
return u.orm.Delete(&in).Error
}
在 /module/user
底下建立一個名為 service
的資料夾,並在裡面建立一個名為 service.go
的檔案,在此處我們會 implement 前面步驟宣告的 interface
,內容如下
package repository
import (
"sample_api/model"
"sample_api/module/user"
)
type UserService struct {
repo user.Repository
}
func NewUserService(repo user.Repository) user.Service {
return &UserService{
repo: repo,
}
}
func (u *UserService) GetUserList(data map[string]interface{}) ([]*model.User, error) {
return u.repo.GetUserList(data)
}
func (u *UserService) GetUser(in *model.User) (*model.User, error) {
return u.repo.GetUser(in)
}
func (u *UserService) CreateUser(in *model.User) (*model.User, error) {
return u.repo.CreateUser(in)
}
func (u *UserService) UpdateUser(in *model.User) (*model.User, error) {
return u.repo.UpdateUser(in)
}
func (u *UserService) ModifyUser(in *model.User, data map[string]interface{}) (*model.User, error) {
return u.repo.ModifyUser(in, data)
}
func (u *UserService) DeleteUser(in *model.User) error {
return u.repo.DeleteUser(in)
}
在 /module/user/delivery
底下建立一個名為 http
的資料夾,並在裡面建立一個名為 http.go
的檔案,在此處我們會 implement 前面步驟宣告的 interface
,內容如下
package http
import (
"github.com/astaxie/beego/validation"
cx "github.com/codingXiang/cxgateway/delivery"
"github.com/codingXiang/cxgateway/pkg/e"
"github.com/codingXiang/cxgateway/pkg/i18n"
"github.com/codingXiang/cxgateway/pkg/util"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"sample_api/model"
"sample_api/module/user"
"sample_api/module/user/delivery"
)
const (
MODULE = "user"
)
type UserHttpHandler struct {
i18nMsg i18n.I18nMessageHandlerInterface
gateway cx.HttpHandler
svc user.Service
}
func NewUserHttpHandler(gateway cx.HttpHandler, svc user.Service) delivery.HttpHandler {
var handler = &UserHttpHandler{
i18nMsg: i18n.NewI18nMessageHandler(MODULE),
gateway: gateway,
svc: svc,
}
/*
v1 版本的 User API
*/
v1 := gateway.GetApiRoute().Group("/v1/user")
v1.GET("", e.Wrapper(handler.GetUserList))
v1.GET("/:id", e.Wrapper(handler.GetUser))
v1.POST("", e.Wrapper(handler.CreateUser))
v1.PUT("/:id", e.Wrapper(handler.UpdateUser))
v1.PATCH("/:id", e.Wrapper(handler.ModifyUser))
v1.DELETE("/:id", e.Wrapper(handler.DeleteUser))
return handler
}
func (g *UserHttpHandler) GetUserList(c *gin.Context) error {
var (
data = map[string]interface{}{}
)
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
//抓取 query string
if in, isExist := c.GetQuery("id"); isExist {
data["id"] = in
}
if in, isExist := c.GetQuery("email"); isExist {
data["email"] = in
}
if in, isExist := c.GetQuery("phone"); isExist {
data["phone"] = in
}
if result, err := g.svc.GetUserList(data); err != nil {
return g.i18nMsg.GetError(err)
} else {
c.JSON(g.i18nMsg.GetSuccess(result))
return nil
}
}
func (g *UserHttpHandler) GetUser(c *gin.Context) error {
var (
data = new(model.User)
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
if result, err := g.svc.GetUser(data); err != nil {
return g.i18nMsg.GetError(err)
} else {
c.JSON(g.i18nMsg.GetSuccess(result))
}
return nil
}
func (g *UserHttpHandler) CreateUser(c *gin.Context) error {
var (
valid = new(validation.Validation)
data = new(model.User)
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
//綁定參數
var err = c.ShouldBindWith(&data, binding.JSON)
if err != nil || data == nil {
return g.i18nMsg.ParameterFormatError()
}
//驗證表單資訊是否填寫充足
valid.Required(&data.ID, "id")
valid.Required(&data.Email, "email")
if err := util.NewRequestHandler().ValidValidation(valid); err != nil {
return err
}
if result, err := g.svc.CreateUser(data); err != nil {
return g.i18nMsg.CreateError(err)
} else {
c.JSON(g.i18nMsg.CreateSuccess(result))
return nil
}
}
func (g *UserHttpHandler) UpdateUser(c *gin.Context) error {
var (
data = new(model.User)
err error
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
//取得 tenant
if data, err = g.svc.GetUser(data); err != nil {
return g.i18nMsg.GetError(err)
}
//綁定參數
err = c.ShouldBindWith(data, binding.JSON)
if err != nil || data == nil {
return g.i18nMsg.ParameterFormatError()
}
//更新 tenant
if result, err := g.svc.UpdateUser(data); err != nil {
return g.i18nMsg.UpdateError(err)
} else {
c.JSON(g.i18nMsg.UpdateSuccess(result))
return nil
}
}
func (g *UserHttpHandler) ModifyUser(c *gin.Context) error {
var (
data = new(model.User)
updateData = new(map[string]interface{})
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
//綁定參數
err := c.ShouldBindWith(&updateData, binding.JSON)
if err != nil || data == nil {
return g.i18nMsg.ParameterFormatError()
}
if result, err := g.svc.ModifyUser(data, *updateData); err != nil {
return g.i18nMsg.ModifyError(err)
} else {
c.JSON(g.i18nMsg.ModifySuccess(result))
return nil
}
}
func (g *UserHttpHandler) DeleteUser(c *gin.Context) error {
var (
data = new(model.User)
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
if err := g.svc.DeleteUser(data); err != nil {
return g.i18nMsg.DeleteError(err)
} else {
c.JSON(g.i18nMsg.DeleteSuccess(nil))
return nil
}
}
在根目錄建立一個名為 config
的資料夾
在底下建立 config.yaml
,分別填入以下內容
applicatoin 的部分是整個 api 的設定,裡面包含對外的 port
,gin
運行的 mode
等等
application:
timeout:
read: 1000
write: 1000
port: 8888
uploadPath: "./upload"
mode: "debug"
appId: "ops"
apiBaseRoute: "/api"
設定 i18n
開頭,依照需求 defaultLanguage
設定為繁體中文,設定檔案在 ./i18n
底下,翻譯檔類型為 yaml
i18n:
defaultLanguage: "zh-Hant"
file:
path: "./i18n"
type: "yaml"
設定 log
開頭,等級設定為 info
,輸出格式 format
為 json
log:
level: "info"
format: "json"
path: "log"
filename: "user.log"
在 config
資料夾底下建立 database.yaml
,並填入以下內容
database:
url: '127.0.0.1'
port: 3306
name: 'sample'
username: 'sample'
password: 'sample'
type: 'mysql'
tablePrefix: ""
maxOpenConns: 1000
maxIdleConns: 1000
maxLifeTime: 5
logMode: true
version: "0.0.2"
多語系檔案設定可以參考 這裡
在 /module/user
底下建立一個名為 repository
的資料夾,並在裡面建立一個名為 repository.go
的檔案,在此處我們會 implement 前面步驟宣告的 interface
,內容如下
package repository
import (
"github.com/jinzhu/gorm"
"sample_api/model"
"sample_api/module/user"
)
type UserRepository struct {
orm *gorm.DB
}
func NewUserRepository(orm *gorm.DB) user.Repository {
return &UserRepository{
orm: orm,
}
}
func (u *UserRepository) GetUserList(data map[string]interface{}) ([]*model.User, error) {
var (
err error
in = make([]*model.User, 0)
)
err = u.orm.Find(&in, data).Error
return in, err
}
func (u *UserRepository) GetUser(in *model.User) (*model.User, error) {
var err error
err = u.orm.First(&in).Error
return in, err
}
func (u *UserRepository) CreateUser(in *model.User) (*model.User, error) {
var err error
err = u.orm.Create(&in).Error
return in, err
}
func (u *UserRepository) UpdateUser(in *model.User) (*model.User, error) {
var err error
err = u.orm.Save(&in).Error
return in, err
}
func (u *UserRepository) ModifyUser(in *model.User, data map[string]interface{}) (*model.User, error) {
var err error
err = u.orm.Model(&in).Updates(data).Error
return in, err
}
func (u *UserRepository) DeleteUser(in *model.User) error {
return u.orm.Delete(&in).Error
}
在 /module/user
底下建立一個名為 service
的資料夾,並在裡面建立一個名為 service.go
的檔案,在此處我們會 implement 前面步驟宣告的 interface
,內容如下
package repository
import (
"sample_api/model"
"sample_api/module/user"
)
type UserService struct {
repo user.Repository
}
func NewUserService(repo user.Repository) user.Service {
return &UserService{
repo: repo,
}
}
func (u *UserService) GetUserList(data map[string]interface{}) ([]*model.User, error) {
return u.repo.GetUserList(data)
}
func (u *UserService) GetUser(in *model.User) (*model.User, error) {
return u.repo.GetUser(in)
}
func (u *UserService) CreateUser(in *model.User) (*model.User, error) {
return u.repo.CreateUser(in)
}
func (u *UserService) UpdateUser(in *model.User) (*model.User, error) {
return u.repo.UpdateUser(in)
}
func (u *UserService) ModifyUser(in *model.User, data map[string]interface{}) (*model.User, error) {
return u.repo.ModifyUser(in, data)
}
func (u *UserService) DeleteUser(in *model.User) error {
return u.repo.DeleteUser(in)
}
在 /module/user/delivery
底下建立一個名為 http
的資料夾,並在裡面建立一個名為 http.go
的檔案,在此處我們會 implement 前面步驟宣告的 interface
,內容如下
package http
import (
"github.com/astaxie/beego/validation"
cx "github.com/codingXiang/cxgateway/delivery"
"github.com/codingXiang/cxgateway/pkg/e"
"github.com/codingXiang/cxgateway/pkg/i18n"
"github.com/codingXiang/cxgateway/pkg/util"
"github.com/gin-gonic/gin"
"github.com/gin-gonic/gin/binding"
"sample_api/model"
"sample_api/module/user"
"sample_api/module/user/delivery"
)
const (
MODULE = "user"
)
type UserHttpHandler struct {
i18nMsg i18n.I18nMessageHandlerInterface
gateway cx.HttpHandler
svc user.Service
}
func NewUserHttpHandler(gateway cx.HttpHandler, svc user.Service) delivery.HttpHandler {
var handler = &UserHttpHandler{
i18nMsg: i18n.NewI18nMessageHandler(MODULE),
gateway: gateway,
svc: svc,
}
/*
v1 版本的 User API
*/
v1 := gateway.GetApiRoute().Group("/v1/user")
v1.GET("", e.Wrapper(handler.GetUserList))
v1.GET("/:id", e.Wrapper(handler.GetUser))
v1.POST("", e.Wrapper(handler.CreateUser))
v1.PUT("/:id", e.Wrapper(handler.UpdateUser))
v1.PATCH("/:id", e.Wrapper(handler.ModifyUser))
v1.DELETE("/:id", e.Wrapper(handler.DeleteUser))
return handler
}
func (g *UserHttpHandler) GetUserList(c *gin.Context) error {
var (
data = map[string]interface{}{}
)
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
//抓取 query string
if in, isExist := c.GetQuery("id"); isExist {
data["id"] = in
}
if in, isExist := c.GetQuery("email"); isExist {
data["email"] = in
}
if in, isExist := c.GetQuery("phone"); isExist {
data["phone"] = in
}
if result, err := g.svc.GetUserList(data); err != nil {
return g.i18nMsg.GetError(err)
} else {
c.JSON(g.i18nMsg.GetSuccess(result))
return nil
}
}
func (g *UserHttpHandler) GetUser(c *gin.Context) error {
var (
data = new(model.User)
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
if result, err := g.svc.GetUser(data); err != nil {
return g.i18nMsg.GetError(err)
} else {
c.JSON(g.i18nMsg.GetSuccess(result))
}
return nil
}
func (g *UserHttpHandler) CreateUser(c *gin.Context) error {
var (
valid = new(validation.Validation)
data = new(model.User)
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
//綁定參數
var err = c.ShouldBindWith(&data, binding.JSON)
if err != nil || data == nil {
return g.i18nMsg.ParameterFormatError()
}
//驗證表單資訊是否填寫充足
valid.Required(&data.ID, "id")
valid.Required(&data.Email, "email")
if err := util.NewRequestHandler().ValidValidation(valid); err != nil {
return err
}
if result, err := g.svc.CreateUser(data); err != nil {
return g.i18nMsg.CreateError(err)
} else {
c.JSON(g.i18nMsg.CreateSuccess(result))
return nil
}
}
func (g *UserHttpHandler) UpdateUser(c *gin.Context) error {
var (
data = new(model.User)
err error
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
//取得 tenant
if data, err = g.svc.GetUser(data); err != nil {
return g.i18nMsg.GetError(err)
}
//綁定參數
err = c.ShouldBindWith(data, binding.JSON)
if err != nil || data == nil {
return g.i18nMsg.ParameterFormatError()
}
//更新 tenant
if result, err := g.svc.UpdateUser(data); err != nil {
return g.i18nMsg.UpdateError(err)
} else {
c.JSON(g.i18nMsg.UpdateSuccess(result))
return nil
}
}
func (g *UserHttpHandler) ModifyUser(c *gin.Context) error {
var (
data = new(model.User)
updateData = new(map[string]interface{})
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
//綁定參數
err := c.ShouldBindWith(&updateData, binding.JSON)
if err != nil || data == nil {
return g.i18nMsg.ParameterFormatError()
}
if result, err := g.svc.ModifyUser(data, *updateData); err != nil {
return g.i18nMsg.ModifyError(err)
} else {
c.JSON(g.i18nMsg.ModifySuccess(result))
return nil
}
}
func (g *UserHttpHandler) DeleteUser(c *gin.Context) error {
var (
data = new(model.User)
)
//將 middleware 傳入的 i18n 進行轉換
g.i18nMsg.SetModule(MODULE)
g.i18nMsg.SetCore(util.GetI18nData(c))
data.ID = c.Params.ByName("id")
if err := g.svc.DeleteUser(data); err != nil {
return g.i18nMsg.DeleteError(err)
} else {
c.JSON(g.i18nMsg.DeleteSuccess(nil))
return nil
}
}
在根目錄建立一個名為 config
的資料夾
在底下建立 config.yaml
,分別填入以下內容
applicatoin 的部分是整個 api 的設定,裡面包含對外的 port
,gin
運行的 mode
等等
application:
timeout:
read: 1000
write: 1000
port: 8888
uploadPath: "./upload"
mode: "debug"
appId: "ops"
apiBaseRoute: "/api"
設定 i18n
開頭,依照需求 defaultLanguage
設定為繁體中文,設定檔案在 ./i18n
底下,翻譯檔類型為 yaml
i18n:
defaultLanguage: "zh-Hant"
file:
path: "./i18n"
type: "yaml"
設定 log
開頭,等級設定為 info
,輸出格式 format
為 json
log:
level: "info"
format: "json"
path: "log"
filename: "user.log"
在 config
資料夾底下建立 database.yaml
,並填入以下內容
database:
url: '127.0.0.1'
port: 3306
name: 'sample'
username: 'sample'
password: 'sample'
type: 'mysql'
tablePrefix: ""
maxOpenConns: 1000
maxIdleConns: 1000
maxLifeTime: 5
logMode: true
version: "0.0.2"
多語系檔案設定可以參考 這裡
最後我們要建立程式的入口,在根目錄建立一個 main.go
,首先先設定初始化方法 init
,在初始化方法讀取參數檔
func init () {
var err error
//初始化 configer,設定預設讀取環境變數
config := configer.NewConfigerCore("yaml", "config", "./config", ".")
config.SetAutomaticEnv("")
//初始化 Gateway
cx.Gateway = cx.NewApiGateway("config", config)
//初始化 db 參數
db := configer.NewConfigerCore("yaml", "database", "./config", ".")
db.SetAutomaticEnv("")
configer.Config.AddCore("db", db)
//設定資料庫
if orm.DatabaseORM, err = orm.NewOrm("database", configer.Config.GetCore("db")); err == nil {
// 建立 Table Schema (Module)
logger.Log.Debug("setup table schema")
{
//設定 使用者資料
orm.DatabaseORM.CheckTable(true, &model.User{})
}
} else {
logger.Log.Error(err.Error())
panic(err.Error())
}
}
有了初始化後,接著我們實作 main
方法,在方法內我們分別實作了 repository
、service
與 http handler
,最後透過封裝好的方法 Run
啟動 Server
func main() {
// 建立 Repository
logger.Log.Debug("Create Repository Instance")
var (
db = orm.DatabaseORM.GetInstance()
userRepo = repository.NewUserRepository(db)
)
// 建立 Service
logger.Log.Debug("Create Service Instance")
var (
userSvc = repository2.NewUserService(userRepo)
)
// 建立 Handler (Module)
logger.Log.Debug("Create Http Handler")
{
http.NewUserHttpHandler(cx.Gateway, userSvc)
}
cx.Gateway.Run()
}
程式運行後看到以下結果代表成功拉
[GIN-debug] GET /api/v1/user --> github.com/codingXiang/cxgateway/pkg/e.Wrapper.func1 (8 handlers)
[GIN-debug] GET /api/v1/user/:id --> github.com/codingXiang/cxgateway/pkg/e.Wrapper.func1 (8 handlers)
[GIN-debug] POST /api/v1/user --> github.com/codingXiang/cxgateway/pkg/e.Wrapper.func1 (8 handlers)
[GIN-debug] PUT /api/v1/user/:id --> github.com/codingXiang/cxgateway/pkg/e.Wrapper.func1 (8 handlers)
[GIN-debug] PATCH /api/v1/user/:id --> github.com/codingXiang/cxgateway/pkg/e.Wrapper.func1 (8 handlers)
[GIN-debug] DELETE /api/v1/user/:id --> github.com/codingXiang/cxgateway/pkg/e.Wrapper.func1 (8 handlers)
{"level":"info","msg":"[API Gateway Start Running]","time":"2020-09-14T23:39:04+08:00"}
從今天的範例中可以知道其實實作 RESTful API
很簡單,明天讓我們來詳細的解讀整體的程式碼吧!
程式範例我放在個人的 github 中,有興趣的朋友可以到 https://github.com/codingXiang/sample_api